package com.gitee.jmash.rbac.utils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.Subject;
import org.eclipse.microprofile.jwt.JsonWebToken;
import com.gitee.jmash.common.grpc.GrpcContext;
import com.gitee.jmash.common.security.JmashPrincipal;
import com.gitee.jmash.common.tree.Node;
import com.gitee.jmash.common.tree.Tree;
import com.gitee.jmash.common.utils.UUIDUtil;
import com.gitee.jmash.rbac.RbacFactory;
import com.gitee.jmash.rbac.client.shiro.authz.JmashBaseAuthorizingRealm;
import com.gitee.jmash.rbac.entity.PermEntity;
import com.gitee.jmash.rbac.entity.ResourceEntity;
import com.gitee.jmash.rbac.entity.RoleEntity;
import com.gitee.jmash.rbac.service.PermRead;
import com.gitee.jmash.rbac.service.RoleRead;
import jmash.rbac.protobuf.ResourceType;
import jmash.rbac.protobuf.RolePermSet;

/** 用户授权 */
public class AuthzUtil {
  
  /** 获取认证，加速校验. */
  public static JmashBaseAuthorizingRealm getAuthorizingRealm() {
    DefaultSecurityManager sm = (DefaultSecurityManager) SecurityUtils.getSecurityManager();
    ModularRealmAuthorizer realms = (ModularRealmAuthorizer) sm.getAuthorizer();
    for(Realm realm :  realms.getRealms()) {
      if (realm instanceof JmashBaseAuthorizingRealm) {
        return (JmashBaseAuthorizingRealm)realm;
      }
    }
    return null;
  }

  /** 获取用户拥有的菜单资源。 */
  public static Tree<ResourceEntity, UUID> userMenuResource(Tree<ResourceEntity, UUID> tree) {
    Subject subject = (Subject) GrpcContext.USER_SUBJECT.get();
    Node<ResourceEntity, UUID> root = tree.getRoot();
    JmashBaseAuthorizingRealm realm= getAuthorizingRealm();
    AuthorizationInfo authzinfo = realm.getAuthorizationInfo(subject.getPrincipals());
    JsonWebToken webToken = (JsonWebToken) subject.getPrincipal();
    filter(webToken.getIssuer(), realm, authzinfo, root);
    return tree;
  }

  /** 获取用户拥有的菜单资源。 */
  public static void filter(String tenant, JmashBaseAuthorizingRealm realm,
      AuthorizationInfo authzinfo, Node<ResourceEntity, UUID> parent) {
    if (parent.isLeaf()) {
      return;
    }
    // 检查孩子权限
    List<Node<ResourceEntity, UUID>> childs = parent.getChilds();
    for (int i = childs.size() - 1; i >= 0; i--) {
      Node<ResourceEntity, UUID> node = childs.get(i);
      ResourceEntity entity = node.getEntity();
      // 移除其他
      if (ResourceType.other.equals(entity.getResourceType())) {
        node.remove();
        continue;
      }
      // 菜单检查权限.
      if (ResourceType.menu.equals(entity.getResourceType())) {
        String permCode = getReourcePermCode(tenant, entity);
        // 没有权限.
        if (!realm.isPermitted(authzinfo, permCode)) {
          node.remove();
          continue;
        }
      }
      // 深度遍历，检查孩子的孩子
      filter(tenant, realm, authzinfo, node);
      // 检查自身.
      if (ResourceType.catalog.equals(entity.getResourceType())) {
        if (node.isLeaf()) {
          node.remove();
        }
      }
    }
  }

  /** 获取资源权限。 */
  public static String getReourcePermCode(String tenant, ResourceEntity entity) {
    String codePrefix = PermEntity.permCodePrefix(entity.getModuleCode(), entity.getResourceCode());
    return codePrefix + "list";
  }


  /** 获取当前登录用户授权. */
  public static RolePermSet userRolesPerms(String tenant, JmashPrincipal principal)
      throws Exception {
    // 系统用户
    if (StringUtils.equals(principal.getName(), UUIDUtil.emptyUUID32())) {
      return RolePermSet.newBuilder().build();
    }
    // 其他用户.
    try (RoleRead roleRead = RbacFactory.getRoleRead(tenant);
        PermRead permRead = RbacFactory.getPermRead(tenant)) {
      RolePermSet.Builder rolePermSet = RolePermSet.newBuilder();
      Set<String> scopes = principal.getScope();
      Map<UUID, String> roleMap = roleRead.findUserRoleMap(principal.getNameUUID(), scopes);

      rolePermSet.addAllRoleCodes(roleMap.values());
      Set<String> perms = permRead.findUserPerms(roleMap.keySet());
      if (roleMap.values().stream().collect(Collectors.toSet()).contains(RoleEntity.ADMIN)
          || roleMap.values().stream().collect(Collectors.toSet())
              .contains(RoleEntity.ADMINISTRATOR)) {
        perms.add("*");
      }
      rolePermSet.addAllPermCodes(perms);
      return rolePermSet.build();
    }
  }



}
